package unitTest;

import utils.ByteArrUtils;
import utils.ByteTree;
import ArithmeticObjects.*;

/**
 * unit-test class for arithmetic objects package
 *
 */
public class ArithObjTest {
	
	private static ModularGroup testModularGroup() throws Exception {
		System.out.println("Starting ModularGroup tests...");
		byte[] pbArr = {10};
		ByteTree pBT = ByteTree.CreateLeaf(pbArr);
		byte[] qbArr = {5};
		ByteTree qBT = ByteTree.CreateLeaf(qbArr);
		byte[] gbArr = {2};
		ByteTree gBT = ByteTree.CreateLeaf(gbArr);
		byte[] ebArr = {0};
		ByteTree eBT = ByteTree.CreateLeaf(ebArr);
		
		ByteTree group = ByteTree.CreateNode();
		group = group.AddChildToNode(pBT).AddChildToNode(qBT).AddChildToNode(gBT).AddChildToNode(eBT);
		
		ModularGroup Gq = new ModularGroup(group);
		if (Gq.getP().getValue().intValue() != 10) System.err.println("Modular Group: wrong P value, expected: 10, got: " + Gq.getP());
		if (Gq.getQ().getValue().intValue() != 5) System.err.println("Modular Group: wrong Q value, expected: 5, got: " + Gq.getQ());
		if (Gq.getG().getValue().intValue() != 2) System.err.println("Modular Group: wrong G value, expected: 2, got: " + Gq.getG());
		if (Gq.getE() != 0) System.err.println("Modular Group: wrong E value, expected: 0, got: " + Gq.getE());
		if (Gq.getOrder().getValue().intValue() != 5) System.err.println("Modular Group: wrong order, expected: 5, got: " + Gq.getOrder());
		return Gq;
	}
	
	private static Field testField(int ord) throws Exception {
		System.out.println("Field test:");
		LargeNumberObject order = new LargeNumberObject(new LargeNumber(ord));
		
		Field Zq = new Field(order);
		if (Zq.getOrder().getValue().intValue() != ord) System.err.println("Field: wrong order, expected: " + ord + ", got: " + Zq.getOrder());
		return Zq;
	}

	private static Ring testRing(int ord) throws Exception {
		System.out.println("Ring test:");
		LargeNumberObject order = new LargeNumberObject(new LargeNumber(ord));
		
		Ring Zq = new Ring(order);
		if (Zq.getOrder().getValue().intValue() != ord) System.err.println("Ring: wrong order, expected: " + ord + ", got: " + Zq.getOrder());
		return Zq;
	}
	
	private static void testLargeNumberObject() throws Exception {
		System.out.println("Starting LargeNumberObject tests...");		
		byte[] b1 = {12};
		ByteTree bt1 = ByteTree.CreateLeaf(b1);
		byte[] b2 = {2};
		ByteTree bt2 = ByteTree.CreateLeaf(b2);
		byte[] b3 = {3};
		ByteTree bt3 = ByteTree.CreateLeaf(b3);
		LargeNumberObject lno1 = new LargeNumberObject(bt1);
		LargeNumberObject lno2 = new LargeNumberObject(bt2);
		LargeNumberObject lno3 = new LargeNumberObject(bt3);
		
		if (lno1.add(lno2,lno3).getValue().intValue() != 17) System.err.println("LargeNumberObject: add failed, expected: 17, got: " + lno1.add(lno2,lno3));
		if (lno1.subtract(lno2).getValue().intValue() != 10) System.err.println("LargeNumberObject: subtract failed, expected: 10, got: " + lno1.subtract(lno2));
		if (lno1.multiply(lno2,lno3).getValue().intValue() != 72) System.err.println("LargeNumberObject: multiply failed, expected: 72, got: " + lno1.multiply(lno2,lno3));
		if (lno1.divide(lno2).getValue().intValue() != 6) System.err.println("LargeNumberObject: divide failed, expected: 6, got: " + lno1.divide(lno2));
		if (lno2.power(lno3).getValue().intValue() != 8) System.err.println("LargeNumberObject: power failed, expected: 8, got: " + lno2.power(lno3));
		if (lno1.compareTo(lno2) <= 0) System.err.println("LargeNumberObject: compare failed (larger than)");
		if (lno2.compareTo(lno3) >= 0) System.err.println("LargeNumberObject: compare failed (less than)");
		if (lno2.compareTo(lno2) != 0) System.err.println("LargeNumberObject: compare failed (equals)");
		if (lno1.additiveInverse().getValue().intValue() != 12) System.out.println("LargeNumberObject: inverse failed, expected: 12, got: " + lno1.additiveInverse());
	}
	
//	private static void testBooleanElement() throws Exception {
//		System.out.println("Starting BooleanElement tests...");
//		BooleanElement b = new BooleanElement(true);
//		if (!b.getBooleanValue()) System.err.println("BooleanElement: wrong boolean value, expected: true, got: false");
//	}
	
	public static FieldElement testFieldElement(int n, Field Zq) throws Exception {
		System.out.println("Starting FieldElement tests...");
		byte[] bArr = ByteArrUtils.intToByteArray(n);
		ByteTree BT = ByteTree.CreateLeaf(bArr);
		FieldElement f = new FieldElement(BT,Zq);
		if (f.getOrder().getValue().intValue() != Zq.getOrder().getValue().intValue()) System.err.println("FieldElement: wrong order, expected: " + Zq.getOrder() + ", got: " + f.getOrder());
		return f;
	}
	
	public static void testModularGroupElement(Group Gq) throws Exception {
		System.out.println("Starting ModularGroupElement tests...");
		byte[] bArr = {16};
		ByteTree BT = ByteTree.CreateLeaf(bArr);
		ModularGroupElement m1 = new ModularGroupElement(BT, Gq);
		byte[] bArr2 = {4};
		ByteTree BT2 = ByteTree.CreateLeaf(bArr2);
		ModularGroupElement m2 = new ModularGroupElement(BT2, Gq);
		byte[] bArr3 = {4};
		ByteTree BT3 = ByteTree.CreateLeaf(bArr3);
		ModularGroupElement m3 = new ModularGroupElement(BT3, Gq);
		
		if (m1.add(m2,m3).getValue().intValue() != 24%Gq.getOrder().getValue().intValue())
			System.err.println("ModularGroupElement: add failed, expected: " + 24%Gq.getOrder().getValue().intValue() + " got: " + m1.add(m2,m3));
		if (m1.subtract(m2).getValue().intValue() != 12%Gq.getOrder().getValue().intValue())
			System.err.println("ModularGroupElement: subtract failed, expected: " + 12%Gq.getOrder().getValue().intValue() + " got: " + m1.subtract(m2));
		if (m1.multiply(m2,m3).getValue().intValue() != 256%Gq.getOrder().getValue().intValue())
			System.err.println("ModularGroupElement: multiply failed, expected: " + 256%Gq.getOrder().getValue().intValue() + " got: " + m1.multiply(m2,m3));
		if (m2.divide(m3).getValue().intValue() != 1%Gq.getOrder().getValue().intValue())
			System.err.println("ModularGroupElement: divide failed, expected: " + 1%Gq.getOrder().getValue().intValue() + " got: " + m2.divide(m3));
		if (m2.power(m3).getValue().intValue() != 256%Gq.getOrder().getValue().intValue())
			System.err.println("ModularGroupElement: power failed, expected: " + 256%Gq.getOrder().getValue().intValue() + " got: " + m2.power(m3));
		if (m2.compareTo(m3) != 0)
			System.err.println("ModularGroupElement: compare failed (equals)");
		if (m1.compareTo(m2) == 0)
			System.err.println("ModularGroupElement: compare failed (different)");
		if (m2.additiveInverse().getValue().intValue() != (-4%Gq.getOrder().getValue().intValue()) + Gq.getOrder().getValue().intValue())
			System.err.println("ModularGroupElement: inverse failed, expected: " + (-4%Gq.getOrder().getValue().intValue()) + Gq.getOrder().getValue().intValue()
			+ " got: " + m2.additiveInverse());
		if (m1.getOrder().getValue().intValue() != Gq.getOrder().getValue().intValue())
			System.err.println("ModularGroupElement: get order failed, expected: " + Gq.getOrder().getValue().intValue() + " got: " + m1.getOrder());
	}
	
	private static void testProductGroupElement(Group Gq) throws Exception {
		System.out.println("Starting ProductGroupElement tests...");
		byte[] bArr1 = {11};
		ByteTree BT1 = ByteTree.CreateLeaf(bArr1);
		byte[] bArr2 = {12};
		ByteTree BT2 = ByteTree.CreateLeaf(bArr2);
		byte[] bArr3 = {13};
		ByteTree BT3 = ByteTree.CreateLeaf(bArr3);
		byte[] bArr4 = {14};
		ByteTree BT4 = ByteTree.CreateLeaf(bArr4);
		
		ByteTree left1 = ByteTree.CreateNode();
		ByteTree right1 = ByteTree.CreateNode();
		ByteTree root1 = ByteTree.CreateNode();
		left1 = left1.AddChildToNode(BT1).AddChildToNode(BT2);
		right1 = right1.AddChildToNode(BT3).AddChildToNode(BT4);
		root1 = root1.AddChildToNode(left1).AddChildToNode(right1);
		ProductGroupElement p1 = new ProductGroupElement(root1, Gq);
		
		byte[] bArr5 = {15};
		ByteTree BT5 = ByteTree.CreateLeaf(bArr5);
		byte[] bArr6 = {16};
		ByteTree BT6 = ByteTree.CreateLeaf(bArr6);
		byte[] bArr7 = {17};
		ByteTree BT7 = ByteTree.CreateLeaf(bArr7);
		byte[] bArr8 = {18};
		ByteTree BT8 = ByteTree.CreateLeaf(bArr8);
		
		ByteTree left2 = ByteTree.CreateNode();
		ByteTree right2 = ByteTree.CreateNode();
		ByteTree root2 = ByteTree.CreateNode();
		left2 = left2.AddChildToNode(BT5).AddChildToNode(BT6);
		right2 = right2.AddChildToNode(BT7).AddChildToNode(BT8);
		root2 = root2.AddChildToNode(left2).AddChildToNode(right2);
		ProductGroupElement p2 = new ProductGroupElement(root2, Gq);
		
		System.out.println("ProductGroupElement: Add");
		System.out.println(p1.add(p2));
		System.out.println("ProductGroupElement: Subtract");
		System.out.println(p1.subtract(p2));
		System.out.println("ProductGroupElement: Multiply");
		System.out.println(p1.multiply(p2));
		System.out.println("ProductGroupElement: Divide");
		System.out.println(p1.divide(p2));
		System.out.println("ProductGroupElement: Power");
		System.out.println(p1.power(new LargeNumberObject(new LargeNumber(3))));
		System.out.println("ProductGroupElement: Compare");
		System.out.println(p1.compareTo(p2));
		System.out.println("ProductGroupElement: Inverse");
		System.out.println(p1.additiveInverse());		
	}
	
	private static void testProductRingElement(Ring Zq) throws Exception {
		System.out.println("Starting ProductRingElement tests...");
		byte[] bArr1 = {11};
		ByteTree BT1 = ByteTree.CreateLeaf(bArr1);
		byte[] bArr2 = {12};
		ByteTree BT2 = ByteTree.CreateLeaf(bArr2);
		byte[] bArr3 = {13};
		ByteTree BT3 = ByteTree.CreateLeaf(bArr3);
		byte[] bArr4 = {14};
		ByteTree BT4 = ByteTree.CreateLeaf(bArr4);
		
		ByteTree left1 = ByteTree.CreateNode();
		ByteTree right1 = ByteTree.CreateNode();
		ByteTree root1 = ByteTree.CreateNode();
		left1 = left1.AddChildToNode(BT1).AddChildToNode(BT2);
		right1 = right1.AddChildToNode(BT3).AddChildToNode(BT4);
		root1 = root1.AddChildToNode(left1).AddChildToNode(right1);
		ProductRingElement p1 = new ProductRingElement(root1, Zq);
		
		byte[] bArr5 = {15};
		ByteTree BT5 = ByteTree.CreateLeaf(bArr5);
		byte[] bArr6 = {16};
		ByteTree BT6 = ByteTree.CreateLeaf(bArr6);
		byte[] bArr7 = {17};
		ByteTree BT7 = ByteTree.CreateLeaf(bArr7);
		byte[] bArr8 = {18};
		ByteTree BT8 = ByteTree.CreateLeaf(bArr8);
		
		ByteTree left2 = ByteTree.CreateNode();
		ByteTree right2 = ByteTree.CreateNode();
		ByteTree root2 = ByteTree.CreateNode();
		left2 = left2.AddChildToNode(BT5).AddChildToNode(BT6);
		right2 = right2.AddChildToNode(BT7).AddChildToNode(BT8);
		root2 = root2.AddChildToNode(left2).AddChildToNode(right2);
		ProductRingElement p2 = new ProductRingElement(root2, Zq);
		
		System.out.println("ProductRingElement: Add");
		System.out.println(p1.add(p2));
		System.out.println("ProductRingElement: Subtract");
		System.out.println(p1.subtract(p2));
		System.out.println("ProductRingElement: Multiply");
		System.out.println(p1.multiply(p2));
		System.out.println("ProductRingElement: Divide");
		System.out.println(p1.divide(p2));
		System.out.println("ProductRingElement: Power");
		System.out.println(p1.power(new LargeNumberObject(new LargeNumber(3))));
		System.out.println("ProductRingElement: Compare");
		System.out.println(p1.compareTo(p2));
		System.out.println("ProductRingElement: Inverse");
		System.out.println(p1.additiveInverse());		
	}
	
	public static void testFieldElementArray(Field Zq) throws Exception {
		System.out.println("Starting FieldElementArray tests...");
		byte[] bArr1 = {11};
		ByteTree BT1 = ByteTree.CreateLeaf(bArr1);
		FieldElement f1 = new FieldElement(BT1, Zq);
		byte[] bArr2 = {12};
		ByteTree BT2 = ByteTree.CreateLeaf(bArr2);
		FieldElement f2 = new FieldElement(BT2, Zq);
		byte[] bArr3 = {13};
		ByteTree BT3 = ByteTree.CreateLeaf(bArr3);
		FieldElement f3 = new FieldElement(BT3, Zq);
		byte[] bArr4 = {14};
		ByteTree BT4 = ByteTree.CreateLeaf(bArr4);
		FieldElement f4 = new FieldElement(BT4, Zq);
		
		ByteTree root = ByteTree.CreateNode();
		root = root.AddChildToNode(f1.getByteTree()).AddChildToNode(f2.getByteTree()).AddChildToNode(f3.getByteTree());
		FieldElementArray fa = new FieldElementArray(root, Zq);
		System.out.println(fa.getArraySize());
		System.out.println(fa.getOrder());
		for (int i=0; i<3; i++)
			System.out.println(fa.getElementForIndex(i));
		fa.addElement(f4);
		System.out.println(fa.toString());
	}

	
	public static void main(String[] args) throws Exception 
	{
//		testBooleanElement();
		testLargeNumberObject();
		
		ModularGroup Gq = testModularGroup();
		testModularGroupElement(Gq);
		
		Field Zq = testField(5);
		testFieldElement(5, Zq);
		
		testProductGroupElement(Gq);
		Ring Rq = testRing(7);
		testProductRingElement(Rq);
		
		testFieldElementArray(Zq);
		
	}
}
